using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Xml;
using System.IO;
using System.Xml.Serialization;
using System.Diagnostics;
using C4F.DevKit.Telephony.Properties;
using System.ComponentModel;


namespace C4F.DevKit.Telephony
{
    [ToolboxItem(true), System.Drawing.ToolboxBitmap(typeof(TapiManager))]
    /// <summary>
    /// This class provides basic telephony functionality.
    /// </summary>
    public class TapiManager : Component
    {
        #region Members

        TapiBase tapiBase = new TapiBase();

        #endregion

        #region Public Properties

        /// <summary>
        /// Represents the currently connected call.
        /// </summary>
        public CallInfo CurrentCall
        {
          
            get { return tapiBase.CurrentCall; }

        }

        /// <summary>
        /// Represents the currently available calls.
        /// </summary>
        public List<CallInfo> AvailableCalls
        {
            
            get { return tapiBase.AvailableCalls; }

        }

        /// <summary>
        /// Represents the name of the "default line".
        /// </summary>
        public string DefaultLineName
        {
            get
            {
                return tapiBase.DefaultLineName;
            }
        }

        #endregion

        #region Delegates and Events

        /// <summary>
        /// Delegate to handle the incoming and outgoing call event.
        /// </summary>
        /// <param name="callInfo">Information about the call which initiated the event.</param>
        public delegate void CallNotificationEventHandler(CallInfo callInfo);

        /// <summary>
        /// This event is raised when there is an incoming call.
        /// </summary>
        public event CallNotificationEventHandler OnIncomingCall;

        /// <summary>
        /// This event is raised when there is an outging call.
        /// </summary>
        public event CallNotificationEventHandler OnOutgoingCall;

        /// <summary>
        /// This event is raised when a call has been connected.
        /// </summary>
        public event CallNotificationEventHandler OnCallConnected;

        /// <summary>
        /// This event is raised when a call has been disconnected.
        /// </summary>
        public event CallNotificationEventHandler OnCallDisconnected;

        /// <summary>
        /// This event is raised when a call is placed on hold.
        /// </summary>
        public event CallNotificationEventHandler OnCallHold;

        /// <summary>
        /// Delegate to handle the digit event.
        /// </summary>
        /// <param name="callInfo">Information about the call which initiated the digit event.</param>
        /// <param name="digit">Received digit.</param>
        public delegate void DigitNotificationEventHandler(CallInfo callInfo, char digit);

        /// <summary>
        /// This event is raised when a digit is received.
        /// </summary>
        public event DigitNotificationEventHandler OnDigitReceived;

        /// <summary>
        /// Delegate to handle the end of playback file event.
        /// </summary>
        /// <param name="callInfo">Information about the call on which the file was being played.</param>
        public delegate void EndOfPlaybackNotificationEventHandler(CallInfo callInfo);

        /// <summary>
        /// This event is raised when the end of playback file event is generated.
        /// </summary>
        public event EndOfPlaybackNotificationEventHandler OnEndOfFilePlayback;

        #endregion

        #region Public Methods

        /// <summary>
        /// Gets the communication lines that are currently available.
        /// </summary>
        /// <returns>List of available lines with audio support.</returns>
        /// <exception cref="TapiManagerException">If there is an error while getting the available address lines.</exception>
        public List<Line> GetAvailableLines()
        {
            return tapiBase.GetAvailableLines();
        }

        /// <summary>
        /// Gets the communication lines that are currently available.
        /// </summary>
        /// <param name="withDefault">Whether or not to include "default" line</param>
        /// <returns>List of available lines with audio support.</returns>
        /// <exception cref="TapiManagerException">If there is an error while getting the available address lines.</exception>
        public List<Line> GetAvailableLines(bool withDefault)
        {
            return tapiBase.GetAvailableLines(withDefault);
        }

        /// <summary>
        /// Makes a call to the specified phone number.
        /// </summary>
        /// <param name="targetAddress">Phone number to be dialed.</param>
        /// <exception cref="ArgumentNullException">The 'targetAddress' is null.</exception>
        /// <exception cref="InvalidOperationException">Another call is already connected.</exception>
        /// <exception cref="TapiManagerException">If there is an error while making the call.</exception>
        public void MakeCall(string targetAddress)
        {
            tapiBase.MakeCall(targetAddress);
        }

        /// <summary>
        /// Makes a call to the specified phone number using the specified address line.
        /// </summary>
        /// <param name="targetAddress">Phone number to be dialed.</param>
        /// <param name="lineName">Name of the line to be used to make the call.</param>
        /// <exception cref="ArgumentNullException">The 'targetAddress' is null (OR) the 'lineName' is null.</exception>
        /// <exception cref="InvalidOperationException">Another call is already connected.</exception>
        /// <exception cref="TapiManagerException">Invalid 'lineName' (OR) the specified line does not support audio (OR) 
        /// there is an error while dialing the phone number.</exception>
        public void MakeCall(string targetAddress, string lineName)
        {
            tapiBase.MakeCall(targetAddress, lineName);
        }

        /// <summary>
        /// Disconnects the current connected call.
        /// </summary>
        /// <exception cref="InvalidOperationException">No call is currently connected.</exception>
        /// <exception cref="TapiManagerException">If there is an error while disconnecting the call.</exception>
        public void DisconnectCall()
        {
            tapiBase.DisconnectCall();
        }

        /// <summary>
        /// Answers the latest incoming call.
        /// </summary>
        /// <exception cref="InvalidOperationException">Another call is already connected (OR) there is no incoming call.</exception>
        /// <exception cref="TapiManagerException">If there is an error while answering the call.</exception>
        public void AnswerCall()
        {
            tapiBase.AnswerCall();
        }

        /// <summary>
        /// Answers an incoming call.
        /// </summary>
        /// <param name="call">Call to be answered.</param>
        /// <exception cref="ArgumentNullException">The 'call' is null.</exception>
        /// <exception cref="InvalidOperationException">Another call is already connected.</exception>
        /// <exception cref="ArgumentException">The 'call' is not valid.</exception>
        /// <exception cref="TapiManagerException">If there is an error while answering the call.</exception>
        public void AnswerCall(CallInfo call)
        {
            tapiBase.AnswerCall(call);
        }

        /// <summary>
        /// Puts the current call on hold.
        /// </summary>
        /// <returns>True if the method succeeds.</returns>
        /// <exception cref="InvalidOperationException">No call is currently connected (OR) the call is already on hold.</exception>
        public bool Hold()
        {
            return tapiBase.Hold();
        }

        /// <summary>
        /// Resumes the current call which is on hold.
        /// </summary>
        /// <returns>True if the method succeeds.</returns>
        /// <exception cref="InvalidOperationException">Current call is not on hold.</exception>
        public bool Resume()
        {
            return tapiBase.Resume();
        }

        /// <summary>
        /// Initializes an  object of TapiManager.
        /// </summary>
        /// <exception cref="TapiManagerException">If there is an error while registering the lines for Tapi events.</exception>
        public TapiManager()
        {
            tapiBase.OnIncomingCall += new TapiBase.CallNotificationEventHandler(tapiBase_OnIncomingCall);
            tapiBase.OnCallConnected += new TapiBase.CallNotificationEventHandler(tapiBase_OnCallConnected);
            tapiBase.OnCallDisconnected += new TapiBase.CallNotificationEventHandler(tapiBase_OnCallDisconnected);
            tapiBase.OnOutgoingCall += new TapiBase.CallNotificationEventHandler(tapiBase_OnOutgoingCall);
            tapiBase.OnCallHold += new TapiBase.CallNotificationEventHandler(tapiBase_OnCallHold);
            tapiBase.OnDigitReceived += new TapiBase.DigitNotificationEventHandler(tapiBase_OnDigitReceived);
            tapiBase.OnEndOfFilePlayback += new TapiBase.EndOfPlaybackNotificationEventHandler(tapiBase_OnEndOfFilePlayback);
        }

        /// <summary>
        /// Starts the digit detection on currently connected call.
        /// </summary>
        /// <exception cref="InvalidOperationException">No call is currently connected (OR) the DTMF is not supported on current line.</exception>
        /// <exception cref="TapiManagerException">If there is an error while registering for digit detection.</exception>
        public void StartDigitDetection()
        {
            tapiBase.StartDigitDetection();
        }

        /// <summary>
        /// Stops the digit detection on currently connected call.
        /// </summary>
        /// <exception cref="InvalidOperationException">No call is currently connected (OR) the DTMF is not supported on current line.</exception>
        /// <exception cref="TapiManagerException">If there is an error while unregistering for digit detection.</exception>
        public void StopDigitDetection()
        {
            tapiBase.StopDigitDetection();
        }

        /// <summary>
        /// This method causes digits to be output on the current call. Valid digits are 0 through 9, '*', '#', 'A', 'B', 'C', and 'D'.
        /// </summary>
        /// <exception cref="InvalidOperationException">No call is currently connected (OR) the DTMF is not supported on current line.</exception>
        /// <exception cref="TapiManagerException">If there is an error while generating the digits.</exception>
        public void GenerateDigits(string digits)
        {
            tapiBase.GenerateDigits(digits);
        }

        /// <summary>
        /// Opens a file dialog and plays selected audio files on the current call.
        /// </summary>
        /// <exception cref="ArgumentException">One of the specified file is not found (OR) one of the specified file is not in 'WAV' format.</exception>
        /// <exception cref="InvalidOperationException">No call is currently connected.</exception>
        /// <exception cref="TapiManagerException">If there is an error while playing the file.</exception>
        public void PlayFiles()
        {
            System.Windows.Forms.OpenFileDialog openFileDialog = new System.Windows.Forms.OpenFileDialog();
            openFileDialog.Filter = "wav files (*.wav)|*.wav";
            openFileDialog.RestoreDirectory = true;
            openFileDialog.Multiselect = true;

            if (openFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                string[] fileNamesArray = openFileDialog.FileNames;
                List<string> fileNamesList = new List<string>();

                foreach (string filename in fileNamesArray)
                {
                    fileNamesList.Add(filename);
                }

                tapiBase.PlayFiles(fileNamesList);
            }
        }

        /// <summary>
        /// Plays Specified audio files on the current call.
        /// </summary>
        /// <param name="filePaths">List of file paths to play.</param>
        /// <exception cref="ArgumentException">One of the specified file is not found (OR) one of the specified file is not in 'WAV' format.</exception>
        /// <exception cref="InvalidOperationException">No call is currently connected.</exception>
        /// <exception cref="TapiManagerException">If there is an error while playing the file.</exception>
        public void PlayFiles(List<string> filePaths)
        {
            tapiBase.PlayFiles(filePaths);
        }

        /// <summary>
        /// Opens a file dialog and plays selected audio file on the current call.
        /// </summary>
        /// <exception cref="InvalidOperationException">No call is currently connected.</exception>
        /// <exception cref="TapiManagerException">If there is an error while playing the file.</exception>
        public void PlayFile()
        {
            System.Windows.Forms.OpenFileDialog openFileDialog = new System.Windows.Forms.OpenFileDialog();
            openFileDialog.Filter = "wav files (*.wav)|*.wav";
            openFileDialog.RestoreDirectory = true;

            if (openFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                tapiBase.PlayFile(openFileDialog.FileName);
            }
        }

        /// <summary>
        /// Plays Specified audio file on the current call.
        /// </summary>
        /// <param name="filePath">Path of the audio file to play.</param>
        /// <exception cref="ArgumentNullException">The 'filePath' is null.</exception>
        /// <exception cref="ArgumentException">The 'filePath' is not specified (OR) the specified file is not found (OR) the specified file is not in 'WAV' format.</exception>
        /// <exception cref="InvalidOperationException">No call is currently connected.</exception>
        /// <exception cref="TapiManagerException">If there is an error while playing the file.</exception>
        public void PlayFile(string filePath)
        {
            tapiBase.PlayFile(filePath);
        }

        /// <summary>
        /// Records the current call and saves with the file name selected in a new save file dialog
        /// </summary>
        /// <exception cref="InvalidOperationException">No call is currently connected.</exception>
        /// <exception cref="ArgumentException">The 'saveFilePath' is not specified (OR) there is an error while creating the target file.</exception>
        /// <exception cref="TapiManagerException">If there is an error while recording the call.</exception>
        public void RecordCall()
        {
            System.Windows.Forms.SaveFileDialog saveFileDialog = new System.Windows.Forms.SaveFileDialog();
            saveFileDialog.RestoreDirectory = true;

            if (saveFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                tapiBase.RecordCall(saveFileDialog.FileName);
            }
        }

        /// <summary>
        /// Records the current call and saves with the specified file name. 
        /// </summary>
        /// <exception cref="InvalidOperationException">No call is currently connected.</exception>
        /// <exception cref="ArgumentNullException">The 'saveFilePath' is null.</exception>
        /// <exception cref="ArgumentException">The 'saveFilePath' is not specified (OR) there is an error while creating the target file.</exception>
        /// <exception cref="TapiManagerException">If there is an error while recording the call.</exception>
        public void RecordCall(string saveFilePath)
        {
            tapiBase.RecordCall(saveFilePath);
        }

        /// <summary>
        /// Stops the recording of the current call.
        /// </summary>
        /// <exception cref="InvalidOperationException">No call is currently connected (OR) the call recording is not in progress.</exception>
        /// <exception cref="TapiManagerException">If there is an error while stopping the call recording.</exception>
        public void StopCallRecording()
        {
            tapiBase.StopCallRecording();
        }

        /// <summary>
        /// Stops the file playingon the current call.
        /// </summary>
        /// <exception cref="InvalidOperationException">No call is currently connected (OR) the playback is not in progress.</exception>
        /// <exception cref="TapiManagerException">If there is an error while stopping the file playing.</exception>
        public void StopPlayingFile()
        {
            tapiBase.StopPlayingFile();
        }

        /// <summary>
        /// Unregisters all the registered lines and then ends the TAPI session.
        /// </summary>
        /// <exception cref="TapiManagerException">If there is an error while unregistering the tapi events.</exception>
        public void TapiShutdown()
        {
            tapiBase.TapiShutdown();
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// This method handles incoming call event.
        /// </summary>
        /// <param name="call">Call object.</param>
        private void tapiBase_OnIncomingCall(CallInfo call)
        {
            OnIncomingCall(call);
        }

        /// <summary>
        /// This method handles incoming call event.
        /// </summary>
        /// <param name="call">Call object.</param>
        private void tapiBase_OnCallConnected(CallInfo call)
        {
            OnCallConnected(call);
        }

        /// <summary>
        /// This method handles incoming call event.
        /// </summary>
        /// <param name="call">Call object.</param>
        private void tapiBase_OnCallDisconnected(CallInfo call)
        {
            OnCallDisconnected(call);
            Thread backgroundThread = new Thread(new ThreadStart(ReinitializeTapi));
            backgroundThread.Start();
        }

        /// <summary>
        /// This method handles incoming call event.
        /// </summary>
        /// <param name="call">Call object.</param>
        private void tapiBase_OnOutgoingCall(CallInfo call)
        {
            OnOutgoingCall(call);
        }

        /// <summary>
        /// This method handles incoming call event.
        /// </summary>
        /// <param name="call">Call object.</param>
        private void tapiBase_OnCallHold(CallInfo call)
        {
            OnCallHold(call);
        }

        /// <summary>
        /// This method handles incoming digits.
        /// </summary>
        /// <param name="call">Call object.</param>
        /// <param name="digit">Received digit.</param>
        private void tapiBase_OnDigitReceived(CallInfo call, char digit)
        {
            OnDigitReceived(call, digit);
        }

        /// <summary>
        /// This method handles the end of playback file event.
        /// </summary>
        /// <param name="call">Call object.</param>
        private void tapiBase_OnEndOfFilePlayback(CallInfo call)
        {
            OnEndOfFilePlayback(call);
        }

        /// <summary>
        /// Re-initializes the tapi session.
        /// </summary>
        private void ReinitializeTapi()
        {
            tapiBase.OnIncomingCall -= new TapiBase.CallNotificationEventHandler(tapiBase_OnIncomingCall);
            tapiBase.OnCallConnected -= new TapiBase.CallNotificationEventHandler(tapiBase_OnCallConnected);
            tapiBase.OnCallDisconnected -= new TapiBase.CallNotificationEventHandler(tapiBase_OnCallDisconnected);
            tapiBase.OnOutgoingCall -= new TapiBase.CallNotificationEventHandler(tapiBase_OnOutgoingCall);
            tapiBase.OnCallHold -= new TapiBase.CallNotificationEventHandler(tapiBase_OnCallHold);
            tapiBase.OnDigitReceived -= new TapiBase.DigitNotificationEventHandler(tapiBase_OnDigitReceived);
            tapiBase.OnEndOfFilePlayback -= new TapiBase.EndOfPlaybackNotificationEventHandler(tapiBase_OnEndOfFilePlayback);

            tapiBase.TapiShutdown();
            tapiBase = new TapiBase();
            tapiBase.OnIncomingCall += new TapiBase.CallNotificationEventHandler(tapiBase_OnIncomingCall);
            tapiBase.OnCallConnected += new TapiBase.CallNotificationEventHandler(tapiBase_OnCallConnected);
            tapiBase.OnCallDisconnected += new TapiBase.CallNotificationEventHandler(tapiBase_OnCallDisconnected);
            tapiBase.OnOutgoingCall += new TapiBase.CallNotificationEventHandler(tapiBase_OnOutgoingCall);
            tapiBase.OnCallHold += new TapiBase.CallNotificationEventHandler(tapiBase_OnCallHold);
            tapiBase.OnDigitReceived += new TapiBase.DigitNotificationEventHandler(tapiBase_OnDigitReceived);
            tapiBase.OnEndOfFilePlayback += new TapiBase.EndOfPlaybackNotificationEventHandler(tapiBase_OnEndOfFilePlayback);
        }

        #endregion

    }
     
}